跳到主要内容

Unity UGUI学习 基础知识

Canvas 画布

在 UGUI 中,其根容器为 Canvas,无论添加任何 UI 到场景中,都会默认生成 Canvas 以及 EventSystem。

所有的 UI 都必须作为 Canvas 的子级进行添加。

来详细的看看 Canvas 的一些设置:(图里的 Canvas 组件)

Render Mode:渲染模式,如下

  • Screen Space-Overlay:2DUI,始终显示在屏幕最前方。
  • Screen Space-Camera:2D及3DUI,绑定到指定摄像机,可显示3D内容,同时UI可以进行3D方面的旋转,UI可以获得3D效果。
  • World Space:3DUI,存在3D空间中的UI。

Pixel Perfect:是否以像素的方式来显示UI。

Sort Order:不同 Canvas 之间的前后显示排列设置。(当设置多个 UI 层时很有用)

Canvas Scaler

UI Scale Mode:UI缩放模式,如下:

1、Constant Pixel Size:像素大小始终不变,即一个 `100100的图片在任何的分辨率下都占用100100` 的像素。一般PC上会使用这种方式,因为PC端分辨率差异并不大。

2、Scale With Screen Size:不关心图片的实际像素大小,而只关心 Width 及 Height 值,这个值如果是 1000,那么 100 高度的图片在任何分辨率下都只占用屏幕 1/10 的尺寸。一般移动端会使用这种方式,因为移动端分辨率差异较大。

3、Constant Physical Size:根据物理单位来进行缩放。

Graphic Raycaster

场景中的射线调整,用于交互事件的配置。

Rect Transform 和Transform 区别

Transform 组件

在 Unity 中,Transform 是 3D 物体必备的基本组件

该组件记录并表示了,一个3D物体 在 三维空间 中的 位置 、 旋转 和 缩放 三种属性

这样的设计,非常便于我们进行游戏开发

在三维场景中,通过 修改 Transform 组件 对应的属性值,完成对3D物体的管控

  • 调整位置,只需修改 Position,即可改变对象出现的位置
  • 调整旋转,只需修改 Rotation,即可改变对象朝向
  • 调整缩放,只需修改 Scale,即可改变对象大小

RectTransform 组件

然而,UI元素所在的是一个二维平面上,直接对应的是我们的 显示器 /手机显示屏

众所周知,我们的显示器、手机屏幕有着不同的大小、不同的品牌,不同的分辨率,不同的屏幕比例等等…

那么就出现了一个相当复杂的问题,这么多不不同同的不同,我们开发者该咋办?

我们怎么保证,一个按钮的位置,显示在不同手机屏幕的左下角?

直接给 UI 元素,设置一个固定的二维坐标,那么在 720*480 的屏幕上是一个位置。

在 1920*1080 屏幕上,又是另一个位置了,肯定不是左下角,还差的很远

于是, Unity 为了解决掉这个让人头疼的问题,就有了 RectTransform 组件(它继承自 Transform 组件)

该组件记录并表示了,一个 2D UI元素 在 屏幕 中的 位置 、 旋转 和 缩放 三种属性

对比 Transform ,它增加了2个新的属性,分别是:Anchor(锚点) 和 Pivot(轴心点)

Rect Transform

Unity 中所有的 GameObject 都必须要携带一个 Transform 组件,且该组件无法移除,而作为 UI 显示的 GameObject 则不是携带Transform 而是使用 Unity 专门为 UI 组件设计的 Rect Transform 组件,如下:

注意:Canvas Renderer 也是 UI 组件必须携带的组件

位置信息

  • 位置:以像素为单位,其具体的值则以锚点为准,表示和锚点距离,其中x轴从左到右的数值越来越大,y轴从下到上的数值越来越大,其中的 Pos Z 和 Transform 中的 position-z 效果一致,但不是使用该值来表示 UI 的深度。保留该值是为了在3D UI的一些效果实现上的考虑。

  • 尺寸:不同于缩放,这两个值设置了 UI 的尺寸,为负并不会翻转 UI 而是直接不显示。

  • 旋转:效果同 Transform,一般配合 Tween 使用。

  • 缩放:效果同 Transform,一般配合 Tween 使用。

如何设置深度 ?

不同于 NGUI 的深度,在 UGUI 中的深度完全使用树形结构来表示,同一层级里位于下方的节点会覆盖掉位于上方的节点,如下:

Anchor 锚点 ⭐

Rect Transform 除了上面提到的功能外,还提供了描点功能,该功能主要实现相对布局的功能。

锚点在 Scene 视图中显示为四个小三角形控制柄,锚点信息也显示在 Inspector 中。

如果矩形变换的父项也是矩形变换,则子矩形变换还可通过各种方式锚定到父矩形变换。例如,子项可以锚定到父项的中心,或锚定到一个角。

如下 UI 元素锚定到父项的中心。元素与中心保持固定偏移。

如下 UI 元素锚定到父项的右下角。元素与右下角保持固定偏移。

通过锚定还可以让子项随父项的宽度或高度一起拉伸。矩形的每个角与其对应的锚点都有一个固定的偏移,即矩形的左上角与左上角锚点有一个固定的偏移,以此类推。因此,矩形的不同角可以锚定到父矩形中的不同点。

UI 元素的左角锚定到父项的左下角并且右角锚定到右下角。元素的角与其各自的锚点保持固定的偏移。

UI 元素的左角锚定到距离父矩形左边一定百分比的点,而右角锚点到距离父矩形右边一定百分比的点。

可以单独拖动每个锚点,如果这些锚点在一起,可以通过单击这些锚点的中间位置并进行拖动来一起拖动锚点。如果在拖动锚点时按住 Shift 键,矩形的相应角将与锚点一起移动。

锚点预设

在 Inspector 中,可在矩形变换组件的左上角找到 Anchor Preset 按钮。单击该按钮将显示 Anchor Presets 下拉选单。从此选单中可以快速选择一些最常用的锚定选项。

它可将 UI 元素锚定到父项的边或中间,或者与父项大小一起拉伸。注意水平锚定和垂直锚定是独立的。

锚点控制柄的一种有用功能是可以自动贴靠到同级矩形的锚点,从而进行精确定位。

同时还可以非常直观的配置描点:

锚点描述的是当前 UI 的父对象的位置信息。而锚点则表示当前对象坐标的(0, 0)点。

我们可以发现有 Left、Center、Right、Top、Middle 及 Bottom 六个设置项,这6个项目可以组合出9种形式的锚点,分别对应了当前UI父对象的 9个方向的位置,这样方便我们的 UI做相对位置的调整,比如当UI的坐标为(0, 0),选择 Center-Middle 则当前UI为居中状态,而选择 Top-Left 则 UI 会位于父对象的左上角。

按住 Ait 键,可以快速让 UI 填满某个方向

除了上面说到的还有一种 stretch 状态,该状态则表示 当前 UI 距离父级 UI 边缘的距离,当设定了之后则是使用一种类似相对位置的方式来定义 UI 的位置及尺寸,我们看下:

当我们选择水平和垂直都为 stretch 时,Pos X、Pos Y、Width 和 Height 都改变为 Left、Top、Right 及 Bottom,即使用了相对位置来排列,我们调整一下看看。

我们发现按钮始终和父级保持10个像素的距离,调整父级的尺寸会修改子级的尺寸,如下:

我们发现按钮始终为一种相对的位置呈现。

自定义 Anchor

我们之前定义锚点都是直接在下拉菜单中选择,那么锚点是否可以自定义,答案是肯定的,比如当我们需要下面的效果时就需要自定义Anchor了。

我们希望子UI的尺寸始终和父UI的尺寸保持一定的比例,比如无论父UI多大,子UI都占其10%的空间。

我们只需要将子UI的锚点设置为一个合适的值即可,比如设置为子UI的尺寸,如下:

复制几个改变大小看看效果:

Anchors (锚点)其实说白了就是:4个点。

在 RectTransform 组件上,锚点 Anchors 有 2个 值Min 和 Max

Min 和 Max 的值是经过 归一化 的,也就是 X 或 Y 的值在0-1 之间

由平面中的 2个坐标点,就可以确定 4个点的位置,而这两个点,就是 Min(x,y)Max(x,y)

锚点重合在一起这种情况下,无论父物体如何改变大小,子物体的大小永远不变

Pivot 轴心

UI 元素的轴心点,或者叫做中心点,它依旧是一个二维坐标点,如果说 Anchor 表示的是 UI 的父级的位置,那么 Pivot 则表示UI本身的 (0, 0) 点的位置。

一般创建一个 UI 元素,默认都是 Pivot (0.5,0.5)

轴心点的原点 (0,0),在当前 UI 元素的左下角

要修改的话,首先开启 Pivot:

旁边那个 Local 可以修改为全局坐标,默认是局部坐标

然后就可以在场景中修改 UI 的 Pivot 了,如下:

这个小蓝圈就是Button组件的中心点,表示Button组件的 (0, 0) 点。,原本是在红点的位置,这里将其的中心点改到左上去了

旋转、大小和缩放修改都是围绕轴心进行的,因此轴心的位置会影响旋转、大小调整或缩放的结果。工具栏 Pivot 按钮设置为轴心模式时,可在 Scene 视图中移动矩形变换的轴心。

下面我们看一个实例:

我希望在屏幕的右上方显示一个按钮且按钮和屏幕的间距为10,只需要将按钮的中心点也设置到右上方即可,如下:

蓝图模式

表示是否忽略掉物体的旋转。点开不能对物体进行旋转。

RawEdit 模式

表示当修改 Anchor 时 UI 的位置及尺寸是否会根据 Anchor 进行匹配,点开可以进行匹配。

锚点与轴心位置的计算

对于 UGUI 元素来说,RectTransform.anchoredPosition(Vector2) 是相对锚点来设置的位置。换句话就是轴心点与锚点的向量,即 UI 坐标。

它根据 AnchorMin 和 AnchorMax 是否重合要分别计算。

  1. 重合:anchoredPosition 就是表示锚点到 Pivot 的位置也就是 Inspector 面板 PosX、PosY 的值
  2. 不重合:轴心点坐标-锚框坐标 anchoredPosition 作用就是修改 UI 对象的二维坐标位置

距离锚点的偏移量

offsetMax、offsetMin —— 偏移量

offsetMax 为当前矩形右上角相对于锚点右上角的偏移。 offsetMin 为当前矩形左下角相对于锚点左下角的偏移。

这个值在使用代码控制 RectTransform 时很有用,比如在制作 UI 时,其中有个 RectTransform 采用的是 “绝对定位”,运行时需要用代码来将其设置为全拉伸,那么对该 RectTransform 执行如下操作就可以实现:

rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.offsetMin = Vector2.zero;
rectTransform.offsetMax = Vector2.zero;

UI 坐标

sizeDelta —— UI坐标

sizeDelta 是 offsetMax - offsetMin 的结果。在锚点全部重合的情况下,它的值就是面板上的(Width,Height)。在锚点完全不重合的情况下,它是相对于父矩形的尺寸。

一个常见的错误是,当 RectTransform 的锚点并非全部重合时,使用 sizeDelta 作为这个 RectTransform 的尺寸。此时拿到的结果一般来说并非预期的结果。

UI 拖拽

public class MoveBag : MonoBehaviour, IDragHandler
{

private RectTransform currentRect;

private void Awake()
{
currentRect = GetComponent<RectTransform>();
}

public void OnDrag(PointerEventData eventData)
{
// 注意,Position 是它的中心点,如果直接修改中心点会导致移动的范围非常的广,所以这里应该修改它中心锚点的坐标
currentRect.anchoredPosition += eventData.delta; // 加上鼠标的偏移
}

}

EventSystem

在UGUI中,EventSystem实现了所有关于交互方面的功能

对于按钮来说,直接有 onClick 的事件,我们可以直接在编辑器中方便的通过拖拽来实现响应函数的赋值,如下:

点击该区域的 + 号,可以添加一个触发项,触发项前面可以选择一个任意的游戏对象,当选择了游戏对象之后后面就可以选择该对象上的一个任意方法(public 修饰的),选择好之后,当我们点击了该按钮对象之后就会调用到选择的游戏对象的制定方法。

或者:将脚本添加到 Canvas 对象上,在脚本里面写上

public class ClickObject : MonoBehaviour
{
void Start ()
{
//获取按钮游戏对象
GameObject btnObj = GameObject.Find ("Canvas/Button");
//获取按钮脚本组件
Button btn = (Button) btnObj.GetComponent<Button>();
//添加点击侦听
btn.onClick.AddListener (onClick);
}

void onClick ()
{
Debug.Log ("click!");
}
}

如果要传递参数,则使用委托关键字 delegate

public class ClickObject2 : MonoBehaviour
{
void Start ()
{
//获取按钮游戏对象
GameObject btnObj = GameObject.Find ("Canvas/Button");
//获取按钮脚本组件
Button btn = (Button) btnObj.GetComponent<Button>();
//添加点击侦听
btn.onClick.AddListener (delegate() {
onClick(btnObj);
});
}

void onClick (GameObject obj)
{
Debug.Log ("click: " + obj.name);
}
}

Event Trigger

我们可以给任意的对象(除了 UI 对象,其它的也可以,具体看下一节)添加 Event Trigger使其拥有响应对应类型的输入的能力,如下,给 Image 对象加上这个Event Trigger 组件:

我们发现其可以支持所有类型的输入事件

事件响应的注册和 onClick 一致就不多说了。

支持任意物体

Event Trigger不仅支持Canvas内的UI,也支持场景中的所有物体。

下面我们看一下如果我想点击场景中的一个 Cube 该怎么办。

我们创建一个新的场景,同时添加一个Cube,首先我们的要响应事件的 GameObject 都需要一个 Collider,如下:

创建Cube自带的Collider,如果没有碰撞器则不能响应事件,这里Is Trigger是否勾选都可以响应输入事件。

我们为 Cube 添加 Event Trigger 组件,同时设定好其鼠标点下时调用一个方法,如下:

这个脚本如下:

public class NewBehaviourScript1 : MonoBehaviour
{
public void PointDownHandler()
{
Debug.Log("Point Down!");
}
}

显示的 Cube 要响应输入,必须为主相机添加响应物理世界的脚本 Physice Raycastor,如下:

最后为场景添加一个Event System即可

运行一下,小方块就可以支持点击响应了。

事件接口

UGUI为我们提供了一些接口,整理如下:

我们新建一个脚本并添加到 Canvas 中的一个 Image上,脚本如下:

using UnityEngine;
using UnityEngine.EventSystems;

public class ImageInteractive : MonoBehaviour, IPointerDownHandler
{
public void OnPointerDown(PointerEventData eventData)
{
Debug.Log(eventData.button);
}
}

这里我们通过输出就可以获得点击的按钮是鼠标左键、中键还是右键了,当然 PointerEventData 还提供了更多的属性来使我们获得更多的点击信息。

注意:这些信息是单纯使用 Event Trigger 无法获得的,同时,使用接口的方式不用添加 Event Trigger 等其他的脚本。

唯一需要注意的就是:脚本必须添加到接收事件的 GameObject 之上。

Reference